﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace TrazandoAndo
{
    public sealed class Tracer
    {
        static object _lockDicThread = new object ();

        static Dictionary<int, ThreadInfo> dicThread = 
            new Dictionary<int, ThreadInfo> ();

        public static int calls = 0;

        public static void enterMethod (string name)
        {
            Interlocked.Increment (ref calls);

            var thread      = Thread.CurrentThread;
            var threadId    = thread.ManagedThreadId;
            var stackInfo   = new StackInfo(name, Environment.TickCount);
            var depth       = 0;
            
            lock (_lockDicThread)
            {
                if (! dicThread.ContainsKey(threadId))
                    dicThread.Add (threadId, getThreadContext(thread));

                ThreadInfo ti = dicThread[threadId];
                depth = ti.stackDepth;
                ti.stack.Push (stackInfo);
            }

            #if COCOLICHE
            string tabs = string.Empty;
            for (int i = 0; i < depth; ++i)
                tabs += "|\t";
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.Write(tabs + ",-> ");
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine(name);
            #endif
        }

        public static void exitMethod (string name, ExitType exitType)
        {
            Interlocked.Increment (ref calls);

            var thread          = Thread.CurrentThread;
            var threadId        = thread.ManagedThreadId;
            var stackInfoStart  = 0;
            var stackInfoName   = string.Empty;
            var depth           = 0;

            lock (_lockDicThread)
            {
                ThreadInfo  threadInfo  = dicThread[threadId];
                StackInfo   stackInfo   = threadInfo.stack.Pop();

                stackInfoStart  = stackInfo.start;
                stackInfoName   = stackInfo.name;
                depth           = threadInfo.stackDepth;

                if (depth == 0)
                    dicThread.Remove (threadId);
            }

            #if COCOLICHE
            var runtime = Environment.TickCount - stackInfoStart;
            string tabs = string.Empty;
            for (int i = 0; i < depth; ++i)
                tabs += "|\t";
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.Write(tabs + "`-> ");
            Console.ForegroundColor = ConsoleColor.DarkCyan;
            Console.WriteLine("{0} [{1} msec]", stackInfoName, runtime);
            #endif
        }

        static ThreadInfo getThreadContext (Thread thread)
        {
            return new ThreadInfo (
                thread.ManagedThreadId,
                thread.Name,
                thread.IsBackground,
                thread.IsThreadPoolThread
            );
        }
    }
}
